Nombre de la actividad: Matriz de covarianza, SVC y PCA.
Indicación:
Reproducir la siguiente libreta con un conjunto de datos de tu elección realizando las adaptaciones necesarias al código original. Se deben incluir las anotaciones pertinentes.
Estudiante: María Elena Martínez Manzanares.
Asignatura: Matemáticas para Ciencia de Datos.
Docente: Dra. Olivia Carolina Gutú Ocampo.
%%HTML
<script src="require.js"></script>
#Cargamos las librerias necesarias.
import numpy as np
from matplotlib import pyplot
import plotly.io as pio
import matplotlib.pyplot as plt
from IPython import display
from sklearn.preprocessing import StandardScaler
import pandas as pd
import plotly.express as px
pio.renderers.default='notebook'
Se realizará el ejercicio considerando la base de datos clásica iris disponible en el UCI Machine Learning Repository.
X = pd.read_csv("iris.csv", header=None)
X
| 0 | 1 | 2 | 3 | 4 | |
|---|---|---|---|---|---|
| 0 | 5.1 | 3.5 | 1.4 | 0.2 | Iris-setosa |
| 1 | 4.9 | 3.0 | 1.4 | 0.2 | Iris-setosa |
| 2 | 4.7 | 3.2 | 1.3 | 0.2 | Iris-setosa |
| 3 | 4.6 | 3.1 | 1.5 | 0.2 | Iris-setosa |
| 4 | 5.0 | 3.6 | 1.4 | 0.2 | Iris-setosa |
| ... | ... | ... | ... | ... | ... |
| 145 | 6.7 | 3.0 | 5.2 | 2.3 | Iris-virginica |
| 146 | 6.3 | 2.5 | 5.0 | 1.9 | Iris-virginica |
| 147 | 6.5 | 3.0 | 5.2 | 2.0 | Iris-virginica |
| 148 | 6.2 | 3.4 | 5.4 | 2.3 | Iris-virginica |
| 149 | 5.9 | 3.0 | 5.1 | 1.8 | Iris-virginica |
150 rows × 5 columns
La matriz tiene una característica cualitativa. Para los efectos de este trabajo no es necesaria, por lo cual procedemos a eliminarla.
X.drop(columns=[4],inplace=True)
Estandarizamos los datos.
scaler = StandardScaler()
X_std = scaler.fit_transform(X)
Confirmamos que la media de las columnas sea cero, la desviación y varianza sea 1.
pd.DataFrame(X_std).describe().loc[["mean","std"]]
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| mean | -2.775558e-16 | -5.140333e-16 | 1.154632e-16 | 9.251859e-16 |
| std | 1.003350e+00 | 1.003350e+00 | 1.003350e+00 | 1.003350e+00 |
Salvo errores despreciables, estos números se pueden considerar los valores esperados.
A continuación, en matrices separadas se calculará los datos estandarizados y los datos centrados con el objetivo de hacer una comparación bivariada de ambos conjuntos de datos por medio de un scatterplot.
# Dataframe A
A = X.copy()
# Dataframe A_std
A_std = X_std.copy()
# Dataframe A_centered
A_centered = X - X.mean()
Para poder hacer el scatterplot consideraremos solamente las columnas 0 y 1.
import plotly.graph_objects as go
fig_1 = px.scatter(A, x=0, y=1,
opacity=0.3,
color_discrete_sequence=['blueviolet']
)
fig_2 = px.scatter(A_std, x=0, y=1,
opacity=0.6,
color_discrete_sequence=['orange']
)
fig_3 = px.scatter(A_centered, x=0, y=1,
opacity=0.3,
color_discrete_sequence=['gray']
)
fig = go.Figure(data=fig_1.data + fig_2.data + fig_3.data )
fig.show()
Recordando que la matriz de covarianza esta definida como
$$ \textrm{Cov}(A_{\textrm{std}}):=\frac{1}{n-1}A_{\textrm{std}}A^T_{\textrm{std}}\qquad\textrm{ }(1) $$podemos notar que entre la matriz (1) y $A_{\textrm{std}}^T A_{\textrm{std}}$ existe un factor multiplicativo de diferencia. Se va a analizar como esta diferencia afecta los eigen valores y los eigen vectores de cada matriz.
Comenzamos haciendo los calculos considerando la matriz de covarianza.
Matriz_covarianza = pd.DataFrame(A_std).cov()
Matriz_covarianza
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| 0 | 1.006711 | -0.110103 | 0.877605 | 0.823443 |
| 1 | -0.110103 | 1.006711 | -0.423338 | -0.358937 |
| 2 | 0.877605 | -0.423338 | 1.006711 | 0.969219 |
| 3 | 0.823443 | -0.358937 | 0.969219 | 1.006711 |
from numpy.linalg import eig
auto_valores, auto_vectores = eig(Matriz_covarianza)
print('Autovalores de la matriz de covarianza de A_std = \n', auto_valores)
print('Autovectores de la matriz de covarianza de A_std = \n', auto_vectores)
Autovalores de la matriz de covarianza de A_std = [2.93035378 0.92740362 0.14834223 0.02074601] Autovectores de la matriz de covarianza de A_std = [[ 0.52237162 -0.37231836 -0.72101681 0.26199559] [-0.26335492 -0.92555649 0.24203288 -0.12413481] [ 0.58125401 -0.02109478 0.14089226 -0.80115427] [ 0.56561105 -0.06541577 0.6338014 0.52354627]]
Cambiamos el formato de los auto_vectores para poder manipularlos de una manera más sencilla en las operaciones futuras. En este nuevo formato, las columnas se corresponde con un autovector.
auto_vectores = pd.DataFrame(auto_vectores.T)
auto_vectores
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| 0 | 0.522372 | -0.263355 | 0.581254 | 0.565611 |
| 1 | -0.372318 | -0.925556 | -0.021095 | -0.065416 |
| 2 | -0.721017 | 0.242033 | 0.140892 | 0.633801 |
| 3 | 0.261996 | -0.124135 | -0.801154 | 0.523546 |
Confirmamos que los auto valores y auto vectores son de hecho auto vectores de la matriz de covarianza.
Para realizar esto, para verificar que $v_i$, $\lambda_i$ son auto vectores y auto valores, respectivamente, de la matriz de covarianza $\textrm{Cov}(A_{\textrm{std}})$, debemos comprobar que
$$ \textrm{Cov}(A_{\textrm{std}})v_i - \lambda_i v_i \approx 0 $$con $i=0,1,2,3$.
auto_valores[0] * auto_vectores[0] - Matriz_covarianza.dot(auto_vectores[0])
0 1.380892 1 -0.869887 2 -2.256962 3 0.639026 dtype: float64
auto_valores[1] * auto_vectores[1] - Matriz_covarianza.dot(auto_vectores[1])
0 -0.191212 1 0.102313 2 -0.059583 3 -0.340097 dtype: float64
auto_valores[2] * auto_vectores[2] - Matriz_covarianza.dot(auto_vectores[2])
0 0.034804 1 -0.145814 2 0.136514 3 0.064929 dtype: float64
auto_valores[3] * auto_vectores[3] - Matriz_covarianza.dot(auto_vectores[3])
0 -1.552213 1 0.583006 2 -1.656413 3 -1.619719 dtype: float64
Las cuatro operaciones resultan en errores despreciables que pueden ser considerados valores cero. Por lo tanto, podemos concluir que el objeto "auto_valores" y "autovectores" consisten en efecto en auto valores y auto vectores de la matriz $\textrm{Cov}(A{\textrm{std}})$.
fig = px.scatter(A_std,
x=0,
y=1,
color_discrete_sequence=['orange'],
marginal_x='violin',
marginal_y='violin',
opacity=0.6
)
arrow_1 = go.layout.Annotation(dict(
x= auto_valores[0] * auto_vectores[0][0],
y= auto_valores[0] * auto_vectores[0][1],
xref="x", yref="y",
text="",
showarrow=True,
axref = "x", ayref='y',
ax= 0,
ay= 0,
arrowhead = 3,
arrowwidth=2.5,
arrowcolor=' mediumseagreen',)
)
arrow_2 = go.layout.Annotation(dict(
x= auto_valores[0] * auto_vectores[0][0],
y= auto_valores[0] * auto_vectores[0][1],
xref="x", yref="y",
text="",
showarrow=True,
axref = "x", ayref='y',
ax= 0,
ay= 0,
arrowhead = 3,
arrowwidth=2.5,
arrowcolor='blueviolet',)
)
fig.update_layout(annotations= [arrow_1,arrow_2])
fig.show()
La flecha es el vector de máxima varianza explicada.
Realizamos un ejercicio análogo pero con la matriz $A_{\textrm{std}}^T A_{\textrm{std}}$.
S = A_std.T.dot(A_std) ### La matriz de covarianza es S/(n-1)
S = pd.DataFrame(S)
S
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| 0 | 150.000000 | -16.405387 | 130.763124 | 122.693045 |
| 1 | -16.405387 | 150.000000 | -63.077414 | -53.481613 |
| 2 | 130.763124 | -63.077414 | 150.000000 | 144.413565 |
| 3 | 122.693045 | -53.481613 | 144.413565 | 150.000000 |
auto_valores, auto_vectores = eig(S)
print('Autovalores de la matriz A_std^{T}*A_std = \n', auto_valores)
print('Autovectores de la matriz A_std^{T}*A_std = \n', auto_vectores)
Autovalores de la matriz A_std^{T}*A_std =
[436.62271256 138.18313961 22.10299175 3.09115609]
Autovectores de la matriz A_std^{T}*A_std =
[[ 0.52237162 -0.37231836 -0.72101681 0.26199559]
[-0.26335492 -0.92555649 0.24203288 -0.12413481]
[ 0.58125401 -0.02109478 0.14089226 -0.80115427]
[ 0.56561105 -0.06541577 0.6338014 0.52354627]]
Para encontrar los valores singulares $\sigma_i$ de $A_{\textrm{std}}^T A_{\textrm{std}}$ debemos considerar la siguiente relación
$$ \sigma_i = \sqrt{\lambda_i}, \textrm{ i=0,...,3.} $$print('singular_value_1 =', np.sqrt(auto_valores[0]).round(4))
print('singular_value_2 =', np.sqrt(auto_valores[1]).round(4))
print('singular_value_3 =', np.sqrt(auto_valores[2]).round(4))
print('singular_value_4 =', np.sqrt(auto_valores[3]).round(4))
singular_value_1 = 20.8955 singular_value_2 = 11.7551 singular_value_3 = 4.7014 singular_value_4 = 1.7582
Recordemos que, por lo visto en clase, sabemos que toda matriz real $A$ tiene la forma
$$ A=U\Sigma V^T $$donde $U$ y $V$ son matrices ortogonales y $\Sigma$ es una matriz diagonal de valores singulares.
Lo que realizaremos en esta sección es lo siguiente.
Para los cálculos, sustituiremos la notación de la matriz $\Sigma$ por $S$.
Calculamos los valores singulares de $A_{\textrm{std}}$.
from scipy.linalg import svdvals
print('Valores singulares A_std = ',svdvals(A_std))
Valores singulares A_std = [20.89551896 11.75513248 4.7013819 1.75816839]
from scipy import linalg
U, S, Vt = linalg.svd(A_std, full_matrices=False)
U.shape, S.shape, Vt.shape
((150, 4), (4,), (4, 4))
A continuación, presentamos los valores singulares obtenido por el método SVD. Notemos que son los mismos calculados anteriormente.
print('Valores singulares de A_std = ', S)
Valores singulares de A_std = [20.89551896 11.75513248 4.7013819 1.75816839]
Procedemos a calcular los valores singulares.
Matriz_U = pd.DataFrame(U)
Matriz_Vt = pd.DataFrame(Vt)
componentes_principales = pd.DataFrame(columns=["componente_principal_1",
"componente_principal_2",
"componente_principal_3",
"componente_principal_4"])
for i, componente_principal in list(enumerate(componentes_principales.columns)) :
componentes_principales[componente_principal] = S[i]*Matriz_U[i]
componentes_principales
| componente_principal_1 | componente_principal_2 | componente_principal_3 | componente_principal_4 | |
|---|---|---|---|---|
| 0 | -2.264542 | -0.505704 | 0.121943 | 0.023073 |
| 1 | -2.086426 | 0.655405 | 0.227251 | 0.103208 |
| 2 | -2.367950 | 0.318477 | -0.051480 | 0.027825 |
| 3 | -2.304197 | 0.575368 | -0.098860 | -0.066311 |
| 4 | -2.388777 | -0.674767 | -0.021428 | -0.037397 |
| ... | ... | ... | ... | ... |
| 145 | 1.870522 | -0.382822 | -0.254532 | 0.388890 |
| 146 | 1.558492 | 0.905314 | 0.025382 | 0.221322 |
| 147 | 1.520845 | -0.266795 | -0.179277 | 0.118903 |
| 148 | 1.376391 | -1.016362 | -0.931405 | 0.024146 |
| 149 | 0.959299 | 0.022284 | -0.528794 | -0.163676 |
150 rows × 4 columns
Notar que las columas de $U$ tienen media cero pero los renglones de $V^T$ no necesariamente.
Matriz_U.describe().round(6)
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| count | 150.000000 | 150.000000 | 150.000000 | 150.000000 |
| mean | 0.000000 | 0.000000 | -0.000000 | -0.000000 |
| std | 0.081923 | 0.081923 | 0.081923 | 0.081923 |
| min | -0.132764 | -0.231590 | -0.213578 | -0.277791 |
| 25% | -0.101535 | -0.050006 | -0.042686 | -0.038185 |
| 50% | 0.019839 | -0.000718 | -0.005089 | 0.003124 |
| 75% | 0.064252 | 0.050587 | 0.054821 | 0.051084 |
| max | 0.158366 | 0.226119 | 0.183122 | 0.266877 |
Matriz_Vt.T.describe().round(6)
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| count | 4.000000 | 4.000000 | 4.000000 | 4.000000 |
| mean | 0.351470 | -0.346096 | -0.073927 | -0.034937 |
| std | 0.410639 | 0.416681 | 0.571005 | 0.575939 |
| min | -0.263355 | -0.925556 | -0.633801 | -0.801154 |
| 25% | 0.325940 | -0.510628 | -0.339975 | -0.293390 |
| 50% | 0.543991 | -0.218867 | -0.191463 | 0.068930 |
| 75% | 0.569522 | -0.054336 | 0.074585 | 0.327383 |
| max | 0.581254 | -0.021095 | 0.721017 | 0.523546 |
PCA se aplica a una matriz previamente estandarizada. Es importante estandarizar para realizar un correcto PCA (ver Importance of Feature Scaling).
En este primer paso, calcularemos los componentes principales por medio de scikit-learn y compararemos si estos componentes principales coinciden con los componentes principales calculados anteriormente "a mano".
from sklearn.decomposition import PCA
pca = PCA(n_components=4)
principalComponents = pca.fit_transform(A_std)
principalDf = pd.DataFrame(data = principalComponents,
columns = ["componente_principal_1",
"componente_principal_2",
"componente_principal_3",
"componente_principal_4"])
principalDf
| componente_principal_1 | componente_principal_2 | componente_principal_3 | componente_principal_4 | |
|---|---|---|---|---|
| 0 | -2.264542 | 0.505704 | -0.121943 | -0.023073 |
| 1 | -2.086426 | -0.655405 | -0.227251 | -0.103208 |
| 2 | -2.367950 | -0.318477 | 0.051480 | -0.027825 |
| 3 | -2.304197 | -0.575368 | 0.098860 | 0.066311 |
| 4 | -2.388777 | 0.674767 | 0.021428 | 0.037397 |
| ... | ... | ... | ... | ... |
| 145 | 1.870522 | 0.382822 | 0.254532 | -0.388890 |
| 146 | 1.558492 | -0.905314 | -0.025382 | -0.221322 |
| 147 | 1.520845 | 0.266795 | 0.179277 | -0.118903 |
| 148 | 1.376391 | 1.016362 | 0.931405 | -0.024146 |
| 149 | 0.959299 | -0.022284 | 0.528794 | 0.163676 |
150 rows × 4 columns
Los componentes principales determinados anteriormente eran los siguientes. Podemos notar que son iguales.
componentes_principales
| componente_principal_1 | componente_principal_2 | componente_principal_3 | componente_principal_4 | |
|---|---|---|---|---|
| 0 | -2.264542 | -0.505704 | 0.121943 | 0.023073 |
| 1 | -2.086426 | 0.655405 | 0.227251 | 0.103208 |
| 2 | -2.367950 | 0.318477 | -0.051480 | 0.027825 |
| 3 | -2.304197 | 0.575368 | -0.098860 | -0.066311 |
| 4 | -2.388777 | -0.674767 | -0.021428 | -0.037397 |
| ... | ... | ... | ... | ... |
| 145 | 1.870522 | -0.382822 | -0.254532 | 0.388890 |
| 146 | 1.558492 | 0.905314 | 0.025382 | 0.221322 |
| 147 | 1.520845 | -0.266795 | -0.179277 | 0.118903 |
| 148 | 1.376391 | -1.016362 | -0.931405 | 0.024146 |
| 149 | 0.959299 | 0.022284 | -0.528794 | -0.163676 |
150 rows × 4 columns
principalDf.describe().round(2)
| componente_principal_1 | componente_principal_2 | componente_principal_3 | componente_principal_4 | |
|---|---|---|---|---|
| count | 150.00 | 150.00 | 150.00 | 150.00 |
| mean | -0.00 | -0.00 | -0.00 | 0.00 |
| std | 1.71 | 0.96 | 0.39 | 0.14 |
| min | -2.77 | -2.66 | -0.86 | -0.47 |
| 25% | -2.12 | -0.59 | -0.26 | -0.09 |
| 50% | 0.41 | 0.01 | 0.02 | -0.01 |
| 75% | 1.34 | 0.59 | 0.20 | 0.07 |
| max | 3.31 | 2.72 | 1.00 | 0.49 |
Recordando que una de las propiedades de las componentes principales es
$$std(\sigma_i u_i) = \frac{\sigma_i}{\sqrt{n-1}}$$que se puede interpretar como la máxima desviación estandar explicada, calcularemos estos valores a partir de nuestros datos.
print('std de la componente 1', S[0]/np.sqrt(149))
print('std de la componente 2', S[1]/np.sqrt(149))
print('std de la componente 2', S[2]/np.sqrt(149))
print('std de la componente 2', S[3]/np.sqrt(149))
std de la componente 1 1.7118276126962426 std de la componente 2 0.9630179756979321 std de la componente 2 0.38515221209495876 std de la componente 2 0.14403476662110398
principalDf['cero_column'] = 0
fig = px.scatter(principalDf,
x='componente_principal_1',
y='cero_column',
color_discrete_sequence=['blueviolet'],
opacity=0.3
)
fig.update_xaxes(ticks='outside',
tickwidth=1,
tickcolor='gray',
ticklen=5)
fig.update_xaxes(showgrid=False)
fig.update_yaxes(
scaleanchor = 'x',
constrain='domain'
)
fig.update_layout(yaxis={'visible': False, 'showticklabels': False})
fig.update_traces(mode = 'markers',
hovertemplate = None
)
fig.update_layout(hovermode='x unified')
fig.update_traces(
hovertemplate =' ',
showlegend = False,
)
fig.update_layout(
hoverlabel = dict(
bgcolor = 'LightSteelBlue',
font_size = 40,
)
)
fig.show()
Calcularemos la matriz con rango 1 que mejor aproxima a la matriz $A$.
Gráfica de matriz de rango 1 que mejor aproxima a $A$, $A_1 = \sigma_1 u_1 v_1^T$.
Observación. La matriz de componentes principales obtenida con scikit-learn es diferente a la obtenida por el algoritmo de scipy. Esto se puede comprobar con la siguiente línea de código.bold text
((U@np.diag(S))==(principalComponents)).all()
False
Dado que en las siguientes operaciones necesitamos utilizar la matriz Vt obtenida con scipy, para evitar tener incosistencias en nuestras cuentas, calcularemos la matriz de componentes principales que se obtiene de multiplicar U y S de scipy (para evitar mezclar librerias).
pca_scipy = (U@np.diag(S))
pca_scipy = pd.DataFrame(pca_scipy)
Tenemos el producto de una matriz $150\times 1$ con una de $1 \times 4$, por lo que el resultado resultará en una matriz de $150 \times 4$.
A_1 = pd.DataFrame(np.outer(pca_scipy[0],Vt[0]))
A_1
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| 0 | -1.182932 | 0.596378 | -1.316274 | -1.280850 |
| 1 | -1.089889 | 0.549470 | -1.212743 | -1.180105 |
| 2 | -1.236950 | 0.623611 | -1.376381 | -1.339339 |
| 3 | -1.203647 | 0.606822 | -1.339324 | -1.303279 |
| 4 | -1.247830 | 0.629096 | -1.388486 | -1.351119 |
| ... | ... | ... | ... | ... |
| 145 | 0.977108 | -0.492611 | 1.087248 | 1.057988 |
| 146 | 0.814112 | -0.410436 | 0.905880 | 0.881500 |
| 147 | 0.794446 | -0.400522 | 0.883997 | 0.860207 |
| 148 | 0.718988 | -0.362479 | 0.800033 | 0.778502 |
| 149 | 0.501110 | -0.252636 | 0.557596 | 0.542590 |
150 rows × 4 columns
import plotly.graph_objects as go
fig_1 = px.scatter(A_1, x=0, y=1,
opacity=0.3,
color_discrete_sequence=['blueviolet']
)
fig_2 = px.scatter(A_std, x=0, y=1,
opacity=0.6,
color_discrete_sequence=['orange']
)
fig = go.Figure(data=fig_1.data + fig_2.data)
fig.show()
Notar que
$$A_{\textrm{std}} = \sigma_1 u_1 v_1^T + \sigma_2 u_2 v_2^T + \sigma_3 u_3 v_3^T + \sigma_4 u_4 v_4^T\qquad \textrm{(2)}.$$Calculamos primero el lado derecho de (2).
A_1=pd.DataFrame(np.outer(pca_scipy[0],Vt[0]))
A_2=pd.DataFrame(np.outer(pca_scipy[1],Vt[1])) + A_1
A_3=pd.DataFrame(np.outer(pca_scipy[2],Vt[2])) + A_2
A_4=pd.DataFrame(np.outer(pca_scipy[3],Vt[3])) + A_3
Notemos que las matrices A_std y A_4 son iguales.
A_4
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| 0 | -0.900681 | 1.032057 | -1.341272 | -1.312977 |
| 1 | -1.143017 | -0.124958 | -1.341272 | -1.312977 |
| 2 | -1.385353 | 0.337848 | -1.398138 | -1.312977 |
| 3 | -1.506521 | 0.106445 | -1.284407 | -1.312977 |
| 4 | -1.021849 | 1.263460 | -1.341272 | -1.312977 |
| ... | ... | ... | ... | ... |
| 145 | 1.038005 | -0.124958 | 0.819624 | 1.447956 |
| 146 | 0.553333 | -1.281972 | 0.705893 | 0.922064 |
| 147 | 0.795669 | -0.124958 | 0.819624 | 1.053537 |
| 148 | 0.432165 | 0.800654 | 0.933356 | 1.447956 |
| 149 | 0.068662 | -0.124958 | 0.762759 | 0.790591 |
150 rows × 4 columns
pd.DataFrame(A_std)
| 0 | 1 | 2 | 3 | |
|---|---|---|---|---|
| 0 | -0.900681 | 1.032057 | -1.341272 | -1.312977 |
| 1 | -1.143017 | -0.124958 | -1.341272 | -1.312977 |
| 2 | -1.385353 | 0.337848 | -1.398138 | -1.312977 |
| 3 | -1.506521 | 0.106445 | -1.284407 | -1.312977 |
| 4 | -1.021849 | 1.263460 | -1.341272 | -1.312977 |
| ... | ... | ... | ... | ... |
| 145 | 1.038005 | -0.124958 | 0.819624 | 1.447956 |
| 146 | 0.553333 | -1.281972 | 0.705893 | 0.922064 |
| 147 | 0.795669 | -0.124958 | 0.819624 | 1.053537 |
| 148 | 0.432165 | 0.800654 | 0.933356 | 1.447956 |
| 149 | 0.068662 | -0.124958 | 0.762759 | 0.790591 |
150 rows × 4 columns
Como A_4 y A_std son iguales, los puntos estan sobrepuestos y no se pueden ver diferencias.
fig_1 = px.scatter(A_4, x=0, y=1,
opacity=0.3,
color_discrete_sequence=['blueviolet']
)
fig_2 = px.scatter(A_std, x=0, y=1,
opacity=0.6,
color_discrete_sequence=['orange']
)
fig = go.Figure(data=fig_1.data + fig_2.data)
fig.show()
Cada valor singular explica una parte de la varianza total:
$$T=\frac{\sigma_1^2+\sigma_2^2+\sigma_3^2+\sigma_4^2}{n-1}$$donde $i$-ésitmo valor singular explica la parte $\frac{\sigma_i^2}{n-1}$ de $T$.
Esta es la varianza explicada utilizando el algoritmo de PCA de scikit-learn.
pca.explained_variance_
array([2.93035378, 0.92740362, 0.14834223, 0.02074601])
Al proveernos scipy de la descomposición explicita SVD, podemos realizar el cálculo manualmente y comparar con lo que resulta de scikit-learn.
varianza_explicada_1 = (S[0]*S[0]/(149)).round(4)
varianza_explicada_2 = (S[1]*S[1]/(149)).round(4)
varianza_explicada_3 = (S[2]*S[2]/(149)).round(4)
varianza_explicada_4 = (S[3]*S[3]/(149)).round(4)
print('varianza_explicada_1 = ',varianza_explicada_1)
print('varianza_explicada_2 = ',varianza_explicada_2)
print('varianza_explicada_3 = ',varianza_explicada_3)
print('varianza_explicada_4 = ',varianza_explicada_4)
varianza_explicada_1 = 2.9304 varianza_explicada_2 = 0.9274 varianza_explicada_3 = 0.1483 varianza_explicada_4 = 0.0207
pca.explained_variance_ratio_
array([0.72770452, 0.23030523, 0.03683832, 0.00515193])
radio_varianza_explicada_1 = ((S[0]*S[0])/(S[0]*S[0]+S[1]*S[1]+S[2]*S[2]+S[3]*S[3])).round(4)
radio_varianza_explicada_2 = ((S[1]*S[1])/(S[0]*S[0]+S[1]*S[1]+S[2]*S[2]+S[3]*S[3])).round(4)
radio_varianza_explicada_3 = ((S[2]*S[2])/(S[0]*S[0]+S[1]*S[1]+S[2]*S[2]+S[3]*S[3])).round(4)
radio_varianza_explicada_4 = ((S[3]*S[3])/(S[0]*S[0]+S[1]*S[1]+S[2]*S[2]+S[3]*S[3])).round(4)
print('radio_varianza_explicada_1 = ', radio_varianza_explicada_1)
print('radio-varianza_explicada_2 = ', radio_varianza_explicada_2)
print('radio-varianza_explicada_2 = ', radio_varianza_explicada_3)
print('radio-varianza_explicada_2 = ', radio_varianza_explicada_4)
radio_varianza_explicada_1 = 0.7277 radio-varianza_explicada_2 = 0.2303 radio-varianza_explicada_2 = 0.0368 radio-varianza_explicada_2 = 0.0052